<?php
/**
 * Created by PhpStorm.
 * User: luoshuqi
 * Date: 18-8-26
 * Time: 下午12:56
 */

namespace App\Console\Commands;


use App\Models\SysAction;
use App\Models\SysOperation;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class DumpOperation extends Command
{
    protected $signature = 'dump-operations';

    protected $description = '导出操作';

    private $regex = '#@operation\((\S+),\s*(\S+)(?:,\s*(\d+))?\)#';

    public function handle()
    {
        $operations = $this->dump(__DIR__ . '/../../Http/Controllers/Admin', 'App\Http\Controllers\Admin');

        foreach ($operations as &$group) {
            $sort = 0;
            foreach ($group as $item) {
                $item['sort'] > $sort and $sort = $item['sort'];
            }
            foreach ($group as &$item) {
                $item['sort'] or $item['sort'] = $sort;
            }
            unset($item);
        }
        unset($group);

        $actions = [];
        foreach ($operations as $v) {
            foreach ($v as $v1) {
                $actions = array_merge($actions, $v1['actions']);
            }
        }
        $actions = array_values(array_unique($actions));
        SysAction::query()->whereNotIn('action', $actions)->delete();
        $sql = "INSERT IGNORE INTO sys_actions (action) VALUES " . substr(str_repeat('(?),', count($actions)), 0, -1);
        DB::insert($sql, $actions);

        $action_map = SysAction::query()->pluck('id', 'action');

        foreach (SysOperation::all() as $model) {
            if (isset($operations[$model->group][$model->name])) {
                $action_ids = array_map(function ($v) use ($action_map) {return $action_map[$v];}, $operations[$model->group][$model->name]['actions']);
                $model->actions = implode(',', $action_ids);
                $model->sort = $operations[$model->group][$model->name]['sort'];
                $model->save();
                unset($operations[$model->group][$model->name]);
            } else {
                $model->delete();
            }
        }

        if ($operations) {
            $insert = [];
            foreach ($operations as $k => $v) {
                foreach ($v as $k1 => $v1) {
                    $action_ids = array_map(function ($v) use ($action_map) {return $action_map[$v];}, $v1['actions']);
                    $insert[] = ['name' => $k1, 'group' => $k, 'actions' => implode(',', $action_ids), 'sort' => $v1['sort']];
                }
            }
            SysOperation::query()->insert($insert);
        }
    }

    private function dump(string $dir, string $namespace) {
        $operations = [];
        foreach (new \DirectoryIterator($dir) as $v) {
            if ($v->isDir() && !$v->isDot()) {
                $operations = array_merge($this->dump($v->getRealPath(), $namespace . '\\' . $v->getFilename()), $operations);
            } else if ($v->isFile() && $v->getExtension() === 'php') {
                $rc = new \ReflectionClass($namespace . '\\' . substr($v->getFilename(), 0, -4));
                foreach ($rc->getMethods() as $rm) {
                    if (!$rm->isPublic()) continue;
                    $doc = $rm->getDocComment();
                    if (!$doc || !preg_match_all($this->regex, $doc, $matches)) continue;
                    $n = count($matches[0]);
                    for ($i = 0; $i < $n; ++$i) {
                        $operations[$matches[1][$i]][$matches[2][$i]]['actions'][] = str_replace('App\Http\Controllers\Admin\\', '', $rc->getName()) . '@' . $rm->getName();
                        if (!isset($operations[$matches[1][$i]][$matches[2][$i]]['sort'])) {
                            if (empty($operations[$matches[1][$i]][$matches[2][$i]]['sort'])) {
                                $operations[$matches[1][$i]][$matches[2][$i]]['sort'] = empty($matches[3][$i]) ? 0 : $matches[3][$i];
                            }
                        }
                    }
                }
            }
        }
        return $operations;
    }
}